package soar.test.jpa.share;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalAmount;
import java.util.Optional;
import java.util.Properties;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EntityManangerHandler
{
    private final static Logger logger = LoggerFactory
            .getLogger(EntityManangerHandler.class);

    private String persistence_unit;
    private String user;
    private String password;
    private String host;
    private int port;
    private String database;

    private String hibernate_dialect;
    private String javax_persistence_jdbc_driver;
    private String javax_persistence_jdbc_url;

    public EntityManangerHandler(String persistence_unit, Properties props)
    {
        this.persistence_unit = persistence_unit;

        String db_type = Optional.ofNullable(props.getProperty("db_type"))
                .map(String::trim).map(String::toUpperCase).orElse("");
        switch (db_type)
        {
        case "MYSQL":
            hibernate_dialect = "org.hibernate.dialect.MySQL5Dialect";
            javax_persistence_jdbc_driver = "com.mysql.jdbc.Driver";
            javax_persistence_jdbc_url = "jdbc:mysql://%s:%d/%s"
                    + "?useUnicode=true&characterEncoding=UTF-8";
            user = Optional.ofNullable(props.getProperty("user"))
                    .map(String::trim).orElse("");
            password = Optional.ofNullable(props.getProperty("password"))
                    .map(String::trim).orElse("");
            host = Optional.ofNullable(props.getProperty("host"))
                    .map(String::trim).orElse("");
            port = Optional.ofNullable(props.getProperty("port"))
                    .map(Integer::parseInt).orElse(3306);
            database = Optional.ofNullable(props.getProperty("database"))
                    .map(String::trim).orElse("");
            break;

        case "POSTGRESQL":
            break;

        case "ORACLE":
            break;

        case "SQLSERVER":
            break;
        }
    }

    private static final TemporalAmount CONNECT_TIME_LIMIT = Duration
            .ofSeconds(25);
    // private static final int CONNECT_TIME_LIMIT = 25;
    private Instant last_connect_time = Instant.ofEpochMilli(0);
    // private long last_connect_ts = 0;
    private static final int OPERATE_COUNT_LIMIT = 25 * 60;
    private int operate_count = 0;

    private EntityManagerFactory emf = null;

    public EntityManager createEntityManager()
    {
        Instant now = Instant.now();
        // long now = System.currentTimeMillis() / 1000;
        if (now.isAfter(this.last_connect_time.plus(CONNECT_TIME_LIMIT))
                // if (now - this.last_connect_ts > CONNECT_TIME_LIMIT
                || this.operate_count > OPERATE_COUNT_LIMIT)
        {
            close();

            try
            {
                Properties props = new Properties();
                props.setProperty("hibernate.hbm2ddl.auto", "validate");
                props.setProperty("hibernate.show_sql", "false");
                props.setProperty("hibernate.max_fetch_depth", "3");
                props.setProperty("hibernate.dialect", hibernate_dialect);
                props.setProperty("javax.persistence.jdbc.driver",
                        javax_persistence_jdbc_driver);
                props.setProperty("javax.persistence.jdbc.user", user);
                props.setProperty("javax.persistence.jdbc.password", password);
                props.setProperty("javax.persistence.jdbc.url", String.format(
                        javax_persistence_jdbc_url, host, port, database));

                emf = Persistence.createEntityManagerFactory(persistence_unit,
                        props);
            }
            catch (Exception e)
            {
                logger.error("{}", e);
                close();
                throw e;
            }

            this.last_connect_time = now;
            this.operate_count = 0;
            logger.info("{} reset", persistence_unit);
        }
        ++this.operate_count;

        return emf.createEntityManager();
    }

    public void close()
    {
        if (emf != null)
        {
            try
            {
                emf.close();
            }
            catch (Exception e)
            {
                logger.error("{}", e.toString());
            }
            emf = null;
        }
        this.last_connect_time = Instant.ofEpochMilli(0);
    }
}
